#include "SQLJoinTableSource.h"
#include "SQLAssignItem.h"
#include "SQLExprTableSource.h"

#include "../expr/SQLBinaryOpExpr.h"
#include "../../SQLUtils.h"

#include "../../../utils/FnvHash.h"
#include "../../../utils/StringUtils.h"
#include "../../../utils/instanceof.h"
#include "../../../Exception/FastsqlColumnAmbiguousException.h"
#include "../../../sql/visitor/SQLASTVisitor.h"
#include "../../../utils/listTran.h"
#include "SQLColumnDefinition.h"

// // 声明一个枚举
// ENUM_BEGIN(SQLJoinTableSource_JoinType){
SQLJoinTableSource_JoinType SQLJoinTableSource_JoinType::COMMA = SQLJoinTableSource_JoinType(1, make_string_ptr(","));
SQLJoinTableSource_JoinType SQLJoinTableSource_JoinType::JOIN = SQLJoinTableSource_JoinType(2, make_string_ptr("JOIN"));
SQLJoinTableSource_JoinType SQLJoinTableSource_JoinType::INNER_JOIN = SQLJoinTableSource_JoinType(3, make_string_ptr("INNER JOIN"));
SQLJoinTableSource_JoinType SQLJoinTableSource_JoinType::CROSS_JOIN = SQLJoinTableSource_JoinType(4, make_string_ptr("CROSS JOIN"));
SQLJoinTableSource_JoinType SQLJoinTableSource_JoinType::NATURAL_JOIN = SQLJoinTableSource_JoinType(5, make_string_ptr("NATURAL JOIN"));
SQLJoinTableSource_JoinType SQLJoinTableSource_JoinType::NATURAL_CROSS_JOIN = SQLJoinTableSource_JoinType(6, make_string_ptr("NATURAL CROSS JOIN"));
SQLJoinTableSource_JoinType SQLJoinTableSource_JoinType::NATURAL_LEFT_JOIN = SQLJoinTableSource_JoinType(7, make_string_ptr("NATURAL LEFT JOIN"));
SQLJoinTableSource_JoinType SQLJoinTableSource_JoinType::NATURAL_RIGHT_JOIN = SQLJoinTableSource_JoinType(8, make_string_ptr("NATURAL RIGHT JOIN"));
SQLJoinTableSource_JoinType SQLJoinTableSource_JoinType::NATURAL_INNER_JOIN = SQLJoinTableSource_JoinType(9, make_string_ptr("NATURAL INNER JOIN"));
SQLJoinTableSource_JoinType SQLJoinTableSource_JoinType::LEFT_OUTER_JOIN = SQLJoinTableSource_JoinType(10, make_string_ptr("LEFT JOIN"));
SQLJoinTableSource_JoinType SQLJoinTableSource_JoinType::LEFT_SEMI_JOIN = SQLJoinTableSource_JoinType(11, make_string_ptr("LEFT SEMI JOIN"));
SQLJoinTableSource_JoinType SQLJoinTableSource_JoinType::LEFT_ANTI_JOIN = SQLJoinTableSource_JoinType(12, make_string_ptr("LEFT ANTI JOIN"));
SQLJoinTableSource_JoinType SQLJoinTableSource_JoinType::RIGHT_OUTER_JOIN = SQLJoinTableSource_JoinType(13, make_string_ptr("RIGHT JOIN"));
SQLJoinTableSource_JoinType SQLJoinTableSource_JoinType::FULL_OUTER_JOIN = SQLJoinTableSource_JoinType(14, make_string_ptr("FULL JOIN"));
SQLJoinTableSource_JoinType SQLJoinTableSource_JoinType::STRAIGHT_JOIN = SQLJoinTableSource_JoinType(15, make_string_ptr("STRAIGHT_JOIN"));
SQLJoinTableSource_JoinType SQLJoinTableSource_JoinType::OUTER_APPLY = SQLJoinTableSource_JoinType(16, make_string_ptr("OUTER APPLY"));
SQLJoinTableSource_JoinType SQLJoinTableSource_JoinType::CROSS_APPLY = SQLJoinTableSource_JoinType(17, make_string_ptr("CROSS APPLY"));

// ENUM_END(SQLJoinTableSource_JoinType)
SQLJoinTableSource_JoinType::SQLJoinTableSource_JoinType()
{
}

SQLJoinTableSource_JoinType::SQLJoinTableSource_JoinType(SQLJoinTableSource_JoinType *joinType)
{
  this->name = joinType->name;
  this->mark = joinType->mark;
  this->nameLCase = joinType->nameLCase;
}

SQLJoinTableSource_JoinType::SQLJoinTableSource_JoinType(int mark_, string_ptr name_)
{
  this->name = name_;
  this->mark = mark_;
  this->nameLCase = StringUtils::toLower(name_);
}

BOOL_ptr SQLJoinTableSource_JoinType::operator==(const SQLJoinTableSource_JoinType &ps)
{
  if ((this->name == ps.name) &&
      (this->mark == ps.mark))
  {
    return BOOL::TRUE;
  }
  return BOOL::FALSE;
}

BOOL_ptr SQLJoinTableSource_JoinType::operator!=(const SQLJoinTableSource_JoinType &ps)
{
  if ((this->name != ps.name) ||
      (this->mark != ps.mark))
  {
    return BOOL::TRUE;
  }
  return BOOL::FALSE;
}

SQLJoinTableSource_UDJ::SQLJoinTableSource_UDJ()
{
  arguments = std::make_shared<std::list<SQLExpr_ptr>>();
  columns = std::make_shared<std::list<SQLName_ptr>>();
  sortBy = std::make_shared<std::list<SQLSelectOrderByItem_ptr>>();
  properties = std::make_shared<std::list<SQLAssignItem_ptr>>();
}

void SQLJoinTableSource_UDJ::accept0(SQLASTVisitor_ptr v)
{
  if (v->visit(SharedObject(SQLJoinTableSource_UDJ)))
  {
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLExpr, SQLObject>(arguments);
    acceptChild(v, tmp1);
    SQLObject_list_ptr tmp2 = ListTran::tran_class<SQLName, SQLObject>(columns);
    acceptChild(v, tmp2);
  }
  v->endVisit(SharedObject(SQLJoinTableSource_UDJ));
}

SQLSelectOrderByItem_list_ptr SQLJoinTableSource_UDJ::getSortBy()
{
  return sortBy;
}

SQLJoinTableSource_UDJ::SQLJoinTableSource_UDJ(SQLExpr_ptr function)
    : SQLJoinTableSource_UDJ()
{
  this->function = function;
}

// SQLJoinTableSource_UDJ *SQLJoinTableSource_UDJ::clone()
SQLObject_ptr SQLJoinTableSource_UDJ::clone()
{
  SQLObject_ptr clone_tmp = std::shared_ptr<SQLJoinTableSource_UDJ>(new SQLJoinTableSource_UDJ());
  std::shared_ptr<SQLJoinTableSource_UDJ> x = std::dynamic_pointer_cast<SQLJoinTableSource_UDJ>(clone_tmp);
  // SQLJoinTableSource_UDJ *x = new SQLJoinTableSource_UDJ();
  x->function = std::dynamic_pointer_cast<SQLExpr>(function->clone());
  for (SQLExpr_ptr arg : *arguments)
  {
    SQLExpr_ptr t = std::dynamic_pointer_cast<SQLExpr>(arg->clone());
    t->setParent(x);
    x->arguments->push_back(t);
  }
  x->alias = alias;
  for (SQLName_ptr column : *columns)
  {
    SQLName_ptr t = std::dynamic_pointer_cast<SQLName>(column->clone());
    t->setParent(x);
    x->columns->push_back(t);
  }

  for (SQLAssignItem_ptr property : *properties)
  {
    SQLAssignItem_ptr c = std::dynamic_pointer_cast<SQLAssignItem>(property->clone());
    c->setParent(x);
    x->properties->push_back(c);
  }
  return x;
}

SQLExpr_ptr SQLJoinTableSource_UDJ::getFunction()
{
  return function;
}

void SQLJoinTableSource_UDJ::setFunction(SQLExpr_ptr function)
{
  this->function = function;
}

SQLExpr_list_ptr SQLJoinTableSource_UDJ::getArguments()
{
  return arguments;
}

SQLName_list_ptr SQLJoinTableSource_UDJ::getColumns()
{
  return columns;
}

string_ptr SQLJoinTableSource_UDJ::getAlias()
{
  return alias;
}

void SQLJoinTableSource_UDJ::setAlias(string_ptr alias)
{
  this->alias = alias;
}

SQLAssignItem_list_ptr SQLJoinTableSource_UDJ::getProperties()
{
  return properties;
}

SETCALSS(SQLJoinTableSource);

SQLJoinTableSource::SQLJoinTableSource(string_ptr alias)
{
  // super(alias);
  using_list = std::make_shared<std::list<SQLExpr_ptr>>();
}

SQLJoinTableSource::SQLJoinTableSource()
{
  using_list = std::make_shared<std::list<SQLExpr_ptr>>();
}

SQLJoinTableSource::SQLJoinTableSource(SQLTableSource_ptr left, SQLJoinTableSource_JoinType_ptr joinType, SQLTableSource_ptr right, SQLExpr_ptr condition)
{
  this->setLeft(left);
  this->setJoinType(joinType);
  this->setRight(right);
  this->setCondition(condition);
  using_list = std::make_shared<std::list<SQLExpr_ptr>>();
}

void SQLJoinTableSource::accept0(SQLASTVisitor_ptr visitor)
{
  if (visitor->visit(SharedObject(SQLJoinTableSource)))
  {
    if (left != NULL)
    {
      left->accept(visitor);
    }

    if (right != NULL)
    {
      right->accept(visitor);
    }

    if (condition != NULL)
    {
      condition->accept(visitor);
    }

    for (int i = 0; i < using_list->size(); i++)
    {
      std::list<SQLExpr_ptr>::iterator it = using_list->begin();
      advance(it, i); // 迭代器前进i个元素，注意i是从0开始
      SQLExpr_ptr item = *it;
      if (item != NULL)
      {
        item->accept(visitor);
      }
    }

    if (udj != NULL)
    {
      udj->accept(visitor);
    }
  }

  visitor->endVisit(SharedObject(SQLJoinTableSource));
}

SQLJoinTableSource_UDJ_ptr SQLJoinTableSource::getUdj()
{
  return udj;
}

void SQLJoinTableSource::setUdj(SQLJoinTableSource_UDJ_ptr x)
{
  if (x != NULL)
  {
    x->setParent(SharedObject(SQLJoinTableSource));
  }
  this->udj = x;
}

BOOL_ptr SQLJoinTableSource::isAsof()
{
  return asof;
}

void SQLJoinTableSource::setAsof(BOOL_ptr asof)
{
  this->asof = asof;
}

SQLJoinTableSource_JoinType_ptr SQLJoinTableSource::getJoinType()
{
  return this->joinType;
}

void SQLJoinTableSource::setJoinType(SQLJoinTableSource_JoinType_ptr joinType)
{
  this->joinType = joinType;
}

void SQLJoinTableSource::setImplicitJoinToCross()
{
  if (*joinType == SQLJoinTableSource_JoinType::COMMA)
  {
    joinType = SQLJoinTableSource_JoinType_ptr(new SQLJoinTableSource_JoinType(&SQLJoinTableSource_JoinType::CROSS_JOIN));
  }
  // if (left instanceof SQLJoinTableSource) {
  //     ((SQLJoinTableSource) left).setImplicitJoinToCross();
  // }

  // if (right instanceof SQLJoinTableSource) {
  //     ((SQLJoinTableSource) right).setImplicitJoinToCross();
  // }
}

SQLTableSource_ptr SQLJoinTableSource::getLeft()
{
  return this->left;
}

void SQLJoinTableSource::setLeft(SQLTableSource_ptr left)
{
  if (left != NULL)
  {
    left->setParent(SharedObject(SQLJoinTableSource));
  }
  this->left = left;
}

void SQLJoinTableSource::setLeft(string_ptr tableName, string_ptr alias)
{
  SQLExprTableSource_ptr tableSource;
  if (tableName->length() == 0)
  {
    tableSource = NULL;
  }
  else
  {
    tableSource = SQLExprTableSource_ptr(new SQLExprTableSource(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(tableName)), alias));
  }
  this->setLeft(tableSource);
}

void SQLJoinTableSource::setRight(string_ptr tableName, string_ptr alias)
{
  SQLExprTableSource_ptr tableSource;
  if (tableName->length() == 0)
  {
    tableSource = NULL;
  }
  else
  {
    tableSource = SQLExprTableSource_ptr(new SQLExprTableSource(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(tableName)), alias));
  }
  this->setRight(tableSource);
}

SQLTableSource_ptr SQLJoinTableSource::getRight()
{
  return this->right;
}

void SQLJoinTableSource::setRight(SQLTableSource_ptr right)
{
  if (right != NULL)
  {
    right->setParent(SharedObject(SQLJoinTableSource));
  }
  this->right = right;
}

SQLExpr_ptr SQLJoinTableSource::getCondition()
{
  return this->condition;
}

void SQLJoinTableSource::setCondition(SQLExpr_ptr condition)
{
  if (condition != NULL)
  {
    condition->setParent(SharedObject(SQLJoinTableSource));
  }
  this->condition = condition;
}

void SQLJoinTableSource::addCondition(SQLExpr_ptr condition)
{
  if (this->condition == NULL)
  {
    this->condition = condition;
    setImplicitJoinToCross();
    return;
  }

  // this->condition = SQLBinaryOpExpr->and (this->condition, condition);
}

void SQLJoinTableSource::addConditionnIfAbsent(SQLExpr_ptr condition)
{
  if (this->containsCondition(condition))
  {
    return;
  }
  // this->condition = SQLBinaryOpExpr->and (this->condition, condition);
}

BOOL_ptr SQLJoinTableSource::containsCondition(SQLExpr_ptr condition)
{
  if (this->condition == NULL)
  {
    return BOOL::FALSE;
  }

  if (this->condition == condition)
  {
    return BOOL::FALSE;
  }

  // if (this->condition instanceof SQLBinaryOpExpr) {
  //     return ((SQLBinaryOpExpr) this->condition).contains(condition);
  // }

  return BOOL::FALSE;
}

SQLExpr_list_ptr SQLJoinTableSource::getUsing()
{
  return this->using_list;
}

BOOL_ptr SQLJoinTableSource::isNatural()
{
  return natural;
}

void SQLJoinTableSource::setNatural(BOOL_ptr natural)
{
  this->natural = natural;
}

// void SQLJoinTableSource::output(Appendable* buf) {
//     try {
//         this->left->output(buf);
//         buf->append(' ');
//         buf->append(this->joinType.toString());
//         buf->append(' ');
//         this->right.output(buf);

//         if (this->condition != NULL) {
//             buf->append(" ON ");
//             this->condition.output(buf);
//         }
//     } catch (IOException ex) {
//         throw new FastsqlException("output error", ex);
//     }
// }

BOOL_ptr SQLJoinTableSource::replace(SQLExpr_ptr expr, SQLExpr_ptr target)
{
  if (condition == expr)
  {
    setCondition(target);
    return BOOL::TRUE;
  }

  for (int i = 0; i < using_list->size(); i++)
  {
    std::list<SQLExpr_ptr>::iterator it = using_list->begin();
    advance(it, i);              // 迭代器前进i个元素，注意i是从0开始
    if (it != using_list->end()) // 最后一个不能删除
    {
      if ((*it) == expr)
      {
        target->setParent(SharedObject(SQLJoinTableSource));
        // using_list->set(i, target);
        (*it) = target;
        return BOOL::TRUE;
      }
    }
  }

  return BOOL::FALSE;
}

BOOL_ptr SQLJoinTableSource::replace(SQLTableSource_ptr cmp, SQLTableSource_ptr target)
{
  if (left == cmp)
  {
    if (target == NULL)
    {
      SQLUtils::replaceInParent(SharedObject(SQLJoinTableSource), right);
    }
    else
    {
      setLeft(target);
    }
    return BOOL::TRUE;
  }
  else if (right == cmp)
  {
    if (target == NULL)
    {
      SQLUtils::replaceInParent(SharedObject(SQLJoinTableSource), left);
    }
    else
    {
      setRight(target);
    }
    return BOOL::TRUE;
  }

  return BOOL::FALSE;
}

void SQLJoinTableSource::cloneTo(SQLJoinTableSource_ptr x)
{
  x->alias = alias;

  if (left != NULL)
  {
    x->setLeft(std::dynamic_pointer_cast<SQLTableSource>(left->clone()));
  }

  x->joinType = joinType;

  if (right != NULL)
  {
    x->setRight(std::dynamic_pointer_cast<SQLTableSource>(right->clone()));
  }

  if (condition != NULL)
  {
    x->setCondition(std::dynamic_pointer_cast<SQLExpr>(condition->clone()));
  }

  for (SQLExpr_ptr item : *using_list)
  {
    SQLExpr_ptr item2 = std::dynamic_pointer_cast<SQLExpr>(item->clone());
    item2->setParent(x);
    x->using_list->push_back(item2);
  }

  x->natural = natural;
  x->asof = asof;

  if (udj != NULL)
  {
    x->udj = std::dynamic_pointer_cast<SQLJoinTableSource_UDJ>(udj->clone());
  }
}

SQLObject_ptr SQLJoinTableSource::clone()
{
  SQLObject_ptr clone_tmp = SQLJoinTableSource_ptr(new SQLJoinTableSource());
  SQLJoinTableSource_ptr x = std::dynamic_pointer_cast<SQLJoinTableSource>(clone_tmp);
  // SQLJoinTableSource *x = new SQLJoinTableSource();
  cloneTo(x);
  return x;
}

void SQLJoinTableSource::reverse()
{
  SQLTableSource_ptr temp = left;
  left = right;
  right = temp;

  // if (left instanceof SQLJoinTableSource) {
  //     ((SQLJoinTableSource) left).reverse();
  // }

  // if (right instanceof SQLJoinTableSource) {
  //     ((SQLJoinTableSource) right).reverse();
  // }
}

/**
 * a inner_join (b inner_join c) -&lt; a inner_join b innre_join c
 */
void SQLJoinTableSource::rearrangement()
{
  if (*joinType != SQLJoinTableSource_JoinType::COMMA && *joinType != SQLJoinTableSource_JoinType::INNER_JOIN)
  {
    return;
  }
  // if (right instanceof SQLJoinTableSource) {
  //     SQLJoinTableSource rightJoin = (SQLJoinTableSource) right;

  //     if (rightJoin.joinType != SQLJoinTableSource_JoinType::COMMA && rightJoin.joinType != SQLJoinTableSource_JoinType::INNER_JOIN) {
  //         return;
  //     }

  //     SQLTableSource a = left;
  //     SQLTableSource b = rightJoin.getLeft();
  //     SQLTableSource c = rightJoin.getRight();
  //     SQLExpr on_ab = condition;
  //     SQLExpr on_bc = rightJoin.condition;

  //     setLeft(rightJoin);
  //     rightJoin.setLeft(a);
  //     rightJoin.setRight(b);

  //     BOOL_ptr on_ab_match = BOOL::FALSE;
  //     if (on_ab instanceof SQLBinaryOpExpr) {
  //         SQLBinaryOpExpr on_ab_binaryOpExpr = (SQLBinaryOpExpr) on_ab;
  //         if (on_ab_binaryOpExpr.getLeft() instanceof SQLPropertyExpr
  //                 && on_ab_binaryOpExpr.getRight() instanceof SQLPropertyExpr) {
  //             string_ptr  leftOwnerName = ((SQLPropertyExpr) on_ab_binaryOpExpr.getLeft()).getOwnernName();
  //             string_ptr  rightOwnerName = ((SQLPropertyExpr) on_ab_binaryOpExpr.getRight()).getOwnernName();

  //             if (rightJoin.containsAlias(leftOwnerName) && rightJoin.containsAlias(rightOwnerName)) {
  //                 on_ab_match = BOOL::TRUE;
  //             }
  //         }
  //     }

  //     if (on_ab_match) {
  //         rightJoin.setCondition(on_ab);
  //     } else {
  //         rightJoin.setCondition(NULL);
  //         on_bc = SQLBinaryOpExpr.and(on_bc, on_ab);
  //     }

  //     setRight(c);
  //     setCondition(on_bc);
  // }
}

BOOL_ptr SQLJoinTableSource::contains(SQLTableSource_ptr tableSource, SQLExpr_ptr condition)
{
  if (right == tableSource)
  {
    if (this->condition == condition)
    {
      return BOOL::TRUE;
    }

    if (this->condition != NULL && (this->condition == condition))
    {
      return BOOL::TRUE;
    }
    else
    {
      return BOOL::FALSE;
    }
  }

  // if (left instanceof SQLJoinTableSource) {
  //     SQLJoinTableSource joinLeft = (SQLJoinTableSource) left;

  //     if (tableSource instanceof SQLJoinTableSource) {
  //         SQLJoinTableSource join = (SQLJoinTableSource) tableSource;

  //         if (join.right.equals(right) && this->condition.equals(condition) && joinLeft.right.equals(join.left)) {
  //             return BOOL::TRUE;
  //         }
  //     }

  //     return joinLeft.contains(tableSource, condition);
  // }

  return BOOL::FALSE;
}

BOOL_ptr SQLJoinTableSource::contains(SQLTableSource_ptr tableSource, SQLExpr_ptr condition, SQLJoinTableSource_JoinType_ptr joinType)
{
  if (right == tableSource)
  {
    if (this->condition == condition)
    {
      return BOOL::TRUE;
    }

    if (this->condition != NULL && (this->condition == condition) && this->joinType == joinType)
    {
      return BOOL::TRUE;
    }
    else
    {
      return BOOL::FALSE;
    }
  }

  // if (left instanceof SQLJoinTableSource) {
  //     SQLJoinTableSource joinLeft = (SQLJoinTableSource) left;

  //     if (tableSource instanceof SQLJoinTableSource) {
  //         SQLJoinTableSource join = (SQLJoinTableSource) tableSource;

  //         if (join.right.equals(right)
  //                 && this->condition != NULL && this->condition.equals(join.condition)
  //                 && joinLeft.right.equals(join.left)
  //                 && this->joinType == join.joinType
  //                 && joinLeft.condition != NULL && joinLeft.condition.equals(condition)
  //                 && joinLeft.joinType == joinType) {
  //             return BOOL::TRUE;
  //         }
  //     }

  //     return joinLeft.contains(tableSource, condition, joinType);
  // }

  return BOOL::FALSE;
}

SQLJoinTableSource *SQLJoinTableSource::findJoin(SQLTableSource_ptr tableSource, SQLJoinTableSource_JoinType_ptr joinType)
{
  if (right == tableSource)
  {
    if (this->joinType == joinType)
    {
      return this;
    }
    return NULL;
  }

  // if (left instanceof SQLJoinTableSource) {
  //     return ((SQLJoinTableSource) left).findJoin(tableSource, joinType);
  // }

  return NULL;
}

BOOL_ptr SQLJoinTableSource::containsAlias(string_ptr alias)
{
  if (SQLUtils::nameEquals(this->alias, alias))
  {
    return BOOL::TRUE;
  }

  if (left != NULL && left->containsAlias(alias))
  {
    return BOOL::TRUE;
  }

  if (right != NULL && right->containsAlias(alias))
  {
    return BOOL::TRUE;
  }

  return BOOL::FALSE;
}

SQLColumnDefinition_ptr SQLJoinTableSource::findColumn(string_ptr columnName)
{
  long hash = FnvHash::hashCode64(columnName);
  return findColumn(hash);
}

SQLColumnDefinition_ptr SQLJoinTableSource::findColumn(long columnNameHash)
{
  SQLObject_ptr column = resolveColum(columnNameHash);
  if (instanceof <SQLObject, SQLColumnDefinition>(column))
  {
    return std::dynamic_pointer_cast<SQLColumnDefinition>(column);
  }

  return NULL;
}

SQLObject_ptr SQLJoinTableSource::resolveColum(long columnNameHash)
{
  if (left != NULL)
  {
    SQLObject_ptr column = left->resolveColum(columnNameHash);
    if (column != NULL)
    {
      return column;
    }
  }

  if (right != NULL)
  {
    return right->resolveColum(columnNameHash);
  }

  return NULL;
}

SQLTableSource_ptr SQLJoinTableSource::findTableSourceWithColumn(string_ptr columnName)
{
  long hash = FnvHash::hashCode64(columnName);
  return findTableSourceWithColumn(hash, columnName, 0);
}

SQLJoinTableSource *SQLJoinTableSource::findTableSourceWithColumn(SQLName_ptr a, SQLName_ptr b)
{
  if (left->findTableSourceWithColumn(a) != NULL && right->findTableSourceWithColumn(b) != NULL)
  {
    return this;
  }

  if (right->findTableSourceWithColumn(a) != NULL && left->findTableSourceWithColumn(b) != NULL)
  {
    return this;
  }

  // if (left instanceof SQLJoinTableSource) {
  //     return ((SQLJoinTableSource) left).findTableSourceWithColumn(a, b);
  // }

  // if (right instanceof SQLJoinTableSource) {
  //     return ((SQLJoinTableSource) right).findTableSourceWithColumn(a, b);
  // }

  return NULL;
}

SQLTableSource_ptr SQLJoinTableSource::findTableSourceWithColumn(long columnNameHash, string_ptr name, int option)
{
  SQLTableSource_ptr leftMatch = NULL;
  if (left != NULL)
  {
    leftMatch = left->findTableSourceWithColumn(columnNameHash, name, option);
  }

  // BOOL_ptr checkColumnAmbiguous = (option & SchemaResolveVisitor->Option->CheckColumnAmbiguous->mask) != 0;
  // if (leftMatch != NULL && !checkColumnAmbiguous)
  // {
  //   return leftMatch;
  // }

  SQLTableSource_ptr rightMatch = NULL;
  if (right != NULL)
  {
    rightMatch = right->findTableSourceWithColumn(columnNameHash, name, option);
  }

  if (leftMatch == NULL)
  {
    return rightMatch;
  }

  if (rightMatch == NULL)
  {
    return leftMatch;
  }

  if (!name->empty())
  {
    string_ptr msg = make_string_ptr("Column '" + *name + "' is ambiguous");
    throw new FastsqlColumnAmbiguousException(msg);
  }
  throw new FastsqlColumnAmbiguousException();
}

BOOL_ptr SQLJoinTableSource::match(string_ptr alias_a, string_ptr alias_b)
{
  if (left == NULL || right == NULL)
  {
    return BOOL::FALSE;
  }

  if (left->containsAlias(alias_a) && right->containsAlias(alias_b))
  {
    return BOOL::TRUE;
  }

  if (right->containsAlias(alias_a) && left->containsAlias(alias_b))
  {
    return BOOL::TRUE;
  }
  else
  {
    return BOOL::FALSE;
  }
}

BOOL_ptr SQLJoinTableSource::conditionContainsTable(string_ptr alias)
{
  if (condition == NULL)
  {
    return BOOL::FALSE;
  }

  // if (condition instanceof SQLBinaryOpExpr) {
  //     return ((SQLBinaryOpExpr) condition).conditionContainsTable(alias);
  // }

  return BOOL::FALSE;
}

SQLJoinTableSource *SQLJoinTableSource::join(SQLTableSource_ptr right, SQLJoinTableSource_JoinType_ptr joinType, SQLExpr_ptr condition)
{
  SQLJoinTableSource *joined = new SQLJoinTableSource(SharedObject(SQLJoinTableSource), joinType, right, condition);
  return joined;
}

SQLTableSource_ptr SQLJoinTableSource::findTableSource(long alias_hash)
{
  if (alias_hash == 0)
  {
    return NULL;
  }

  if (aliasHashCode64() == alias_hash)
  {
    return SharedObject(SQLJoinTableSource);
  }

  SQLTableSource_ptr result = left->findTableSource(alias_hash);
  if (result != NULL)
  {
    return result;
  }

  return right->findTableSource(alias_hash);
}

SQLTableSource_ptr SQLJoinTableSource::other(SQLTableSource_ptr x)
{
  if (left == x)
  {
    return right;
  }

  if (right == x)
  {
    return left;
  }

  return NULL;
}

BOOL_ptr SQLJoinTableSource::isGlobal()
{
  return global;
}

void SQLJoinTableSource::setGlobal(BOOL_ptr global)
{
  this->global = global;
}

int SQLJoinTableSource::hashCode()
{
  int result = left != NULL ? left->hashCode() : 0;
  // result = 31 * result + (joinType != NULL ? joinType.hashCode() : 0);
  result = 31 * result + (right != NULL ? right->hashCode() : 0);
  result = 31 * result + (condition != NULL ? condition->hashCode() : 0);
  // result = 31 * result + using_list->hashCode();
  result = 31 * result + (natural ? 1 : 0);
  result = 31 * result + (global ? 1 : 0);
  return result;
}

bool SQLJoinTableSource::equals(Object_ptr o)
{
  if (this == o.get())
  {
    return true;
  }
  if (o == NULL || getClass() != o->getClass())
  {
    return false;
  }

  if (!SQLTableSourceImpl::equals(o))
  {
    return false;
  }

  SQLJoinTableSource *that = dynamic_cast<SQLJoinTableSource *>(o.get());

  if (natural != that->natural)
  {
    return false;
  }
  if (asof != that->asof)
  {
    return false;
  }
  if (global != that->global)
  {
    return false;
  }
  if (left != NULL ? !(left == that->left) : that->left != NULL)
  {
    return false;
  }
  if (joinType != that->joinType)
  {
    return false;
  }
  if (right != NULL ? !(right == that->right) : that->right != NULL)
  {
    return false;
  }
  if (condition != NULL ? !(condition == that->condition) : that->condition != NULL)
  {
    return false;
  }
  if (!using_list->empty() ? !(using_list == that->using_list) : !that->using_list->empty())
  {
    return false;
  }
  if (udj != NULL ? (udj == that->udj) : that->udj == NULL)
  {
    return true;
  }
  else
  {
    return false;
  }
}

void SQLJoinTableSource::splitTo(SQLTableSource_list_ptr  outTableSources, SQLJoinTableSource_JoinType_ptr joinType)
{
  if (joinType == this->joinType)
  {
    if (instanceof <SQLTableSource, SQLJoinTableSource>(left))
    {
      (std::dynamic_pointer_cast<SQLJoinTableSource>(left))->splitTo(outTableSources, joinType);
    }
    else
    {
      outTableSources->push_back(left);
    }

    if (instanceof <SQLTableSource, SQLJoinTableSource>(right))
    {
      (std::dynamic_pointer_cast<SQLJoinTableSource>(right))->splitTo(outTableSources, joinType);
    }
    else
    {
      outTableSources->push_back(right);
    }
  }
  else
  {
    outTableSources->push_back(SharedObject(SQLJoinTableSource));
  }
}
